/*
 *  Copyright (C) 2010 Matthias Buch-Kromann <mbk.isv@cbs.dk>
 * 
 *  This file is part of the MatrixParser package.
 *  
 *  The MatrixParser program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.osdtsystem.matrixparser.features;

import gnu.trove.TFloatArrayList;
import java.util.Iterator;
import java.util.Map.Entry;
import org.osdtsystem.matrixparser.logging.Log;

/**
 *
 * @author Matthias Buch-Kromann <mbk.isv@cbs.dk>
 */
public class DenseFeatureVector extends AbstractFeatureVector {
    TFloatArrayList list;

    public DenseFeatureVector(int size) {
        this.list = new TFloatArrayList(size);
    }

    public DenseFeatureVector() {
        this.list = new TFloatArrayList();
    }

    public final int size() {
        return list.size();
    }

    public final double getValue(int id) {
        if (id < 0 || id > size())
            return 0;
        return list.get(id);
    }

    public final void addValue(Feature f, double v) {
        addValue(f.id(), v);
    }

    public final void addValue(int id, double v) {
        if (id < 0)
            return;
        int size = size();
        if (id >= size) {
            list.ensureCapacity(id + 2);
            list.fill(size, id + 1, 0);
        }
        list.set(id, (float) (v + getValue(id)));
    }

    public final void setValue(int id, double v) {
        if (id < 0)
            return;
        int size = size();
        if (id >= size) {
            list.ensureCapacity(id + 2);
            list.fill(size, id + 1, 0);
        }
        list.set(id, (float) v);
    }

    public final double getValue(Feature f) {
        int id = f.id();
        if (id < 0 || id >= size())
            return 0;
        return list.get(id);
    }

    public final void setValue(Feature f, double v) {
        int id = f.id();
        if (id < 0)
            return;
        setValue(id, v);
    }

    public int checksum() {
        int checksum = 13;
        for (int i = 0; i < size(); ++i) {
            int valueAsInt = Float.floatToIntBits(list.get(i)) ^ 0xf;
            checksum = 19 * checksum + valueAsInt;
        }
        return checksum;
    }

    public String checksum64() {
        return Log.base64(checksum());
    }


    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder().append("DenseFeatureVector: checksum=").append(checksum64()).append(" ");
        String sep = "";
        for (int i = 0; i < Math.min(100, list.size()); ++i) {
            if (i % 20 == 0)
                sb.append("\n\t");
            sb.append(sep).append(String.format("%.2g", list.get(i)));
            sep = " ";
        }
        if (list.size() > 100)
            sb.append("\n\t...");
        sb.append("\n");
        return sb.toString();
    }

    @Override
    public Iterator<Entry<Feature, Double>> iterator() {
        return new DenseFeatureVectorIterator(this);
    }

    public void addValue(FeatureHandler fh, CharSequence chars, double v) {
        addValue(fh.getFeature(chars.toString()), v);
    }

    public class DenseFeatureVectorIterator
                implements Iterator<Entry<Feature, Double>> {
        DenseFeatureVector fv;
        int next = 0;

        public DenseFeatureVectorIterator(DenseFeatureVector fv) {
            this.fv = fv;
        }

        public boolean hasNext() {
            return next < fv.size();
        }

        public Entry<Feature, Double> next() {
            return new FeatureVectorEntry(new Feature(next), list.get(next));
        }

        public void remove() {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    }

    public class FeatureVectorEntry implements Entry<Feature,Double> {
        Feature key;
        float value;

        public FeatureVectorEntry(Feature key, float value) {
            this.key = key;
            this.value = value;
        }

        public Feature getKey() {
            return key;
        }

        public Double getValue() {
            return Double.valueOf((double) value);
        }

        public Double setValue(Double value) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    }

    public void ignoreFeatures(FeatureVector fv) {
        for (Entry<Feature,Double> entry : fv) {
            if (entry.getValue() != 0)
                setValue(entry.getKey(), 0);
        }
    }

    public void add(FeatureVector fv) {
        for (Entry<Feature,Double> entry : fv) {
            if (entry.getValue() != 0) {
                Feature key = entry.getKey();
                setValue(key, getValue(key) + entry.getValue());
            }
        }
    }
}
